package io.techery.progresshint;

import ohos.agp.components.*;
import ohos.agp.utils.Point;

import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;

import ohos.multimodalinput.event.TouchEvent;

public abstract class ProgressHintDelegate implements Slider.ValueChangedListener {
    /**
     * POPUP_FIXED 固定居中
     */
    public static final int POPUP_FIXED = 1;
    /**
     * POPUP_FOLLOW 跟随进度值显示
     */
    public static final int POPUP_FOLLOW = 0;

    /**
     * Slider对象
     */
    protected Slider mSeekBar;
    /**
     * Component对象
     */
    protected Component mPopupView;
    /**
     * Text对象
     */
    protected Text mPopupTextView;
    /**
     * PopupOffset
     */
    protected int mPopupOffset;
    /**
     * PopupStyle
     */
    protected int mPopupStyle;

    private int mPopupLayout;
    private boolean mPopupAlwaysShown;
    private boolean mPopupDraggable;
    private boolean isTracking;
    private SeekBarHintAdapter mHintAdapter;
    private SeekBarHintAttacher mHintAttacher;

    private ProxyChangeListener listener = new ProxyChangeListener();
    private EventHandler handler = new EventHandler(EventRunner.create(true));

    /**
     * ProgressHintDelegate Instance
     *
     * @param seekBar seekBar
     * @param attrSet attrSet
     * @param styleName styleName
     */
    public ProgressHintDelegate(Slider seekBar, AttrSet attrSet, String styleName) {
        int popupLayout = AttrValueUtils.get(attrSet, "popupLayout", ResourceTable.Layout_progress_hint_popup);
        int popupOffset = AttrValueUtils.get(attrSet, "popupOffset", 0);
        int popupStyle = AttrValueUtils.get(attrSet, "popupStyle", ProgressHintDelegate.POPUP_FOLLOW);
        int popupAnimStyle = 0;
        boolean popupAlwaysShown = AttrValueUtils.get(attrSet, "popupAlwaysShown", false);
        boolean popupDraggable = AttrValueUtils.get(attrSet, "popupDraggable", false);
        initDelegate(seekBar, popupLayout, popupOffset, popupAlwaysShown, popupDraggable, popupStyle, popupAnimStyle);
    }

    private void initDelegate(Slider seekBar, int popupLayout, int popupOffset, boolean popupAlwaysShown,
            boolean popupDraggable, int popupStyle, int popupAnimStyle) {
        this.mSeekBar = seekBar;
        this.mPopupLayout = popupLayout;
        this.mPopupOffset = popupOffset;
        this.mPopupAlwaysShown = popupAlwaysShown;
        this.mPopupDraggable = popupDraggable;
        this.mPopupStyle = popupStyle;
        this.mHintAdapter = new MySeekBarHintAdapter();

        initHintPopup();
        attachSeekBar();
    }

    private void initHintPopup() {
        String popupText = null;
        if (mHintAdapter != null) {
            popupText = mHintAdapter.getHint(mSeekBar, mSeekBar.getProgress());
        }

        LayoutScatter inflater = LayoutScatter.getInstance(mSeekBar.getContext());
        mPopupView = inflater.parse(mPopupLayout, null, false);
        mPopupTextView = (Text) mPopupView.findComponentById(ResourceTable.Id_isb_progress);
        mPopupTextView.setText(popupText != null ? popupText : String.valueOf(mSeekBar.getProgress()));
    }

    private void attachSeekBar() {
        ComponentTreeObserver.GlobalLayoutListener layoutListener = new ComponentTreeObserver.GlobalLayoutListener() {
            @Override
            public void onGlobalLayoutUpdated() {
                if (mSeekBar.getVisibility() != Component.VISIBLE) {
                    hidePopup();
                } else {
                    checkInitialState();
                }
            }
        };
        mSeekBar.setBindStateChangedListener(new Component.BindStateChangedListener() {
            @Override
            public void onComponentBoundToWindow(Component component) {
                mSeekBar.setValueChangedListener(listener);
                mSeekBar.getComponentTreeObserver().addTreeLayoutChangedListener(layoutListener);
                if (mHintAttacher != null) {
                    mHintAttacher.onAttached();
                }
            }

            @Override
            public void onComponentUnboundFromWindow(Component component) {
                mSeekBar.getComponentTreeObserver().removeTreeLayoutChangedListener(layoutListener);
                hidePopup();
            }
        });
        this.listener.setInternalListener(this);
    }

    private void checkInitialState() {
        setPopupAlwaysShown(mPopupAlwaysShown);
        setPopupDraggable(mPopupDraggable);
    }

    private Component.TouchEventListener popupTouchProxy = new Component.TouchEventListener() {
        @Override
        public boolean onTouchEvent(Component component, TouchEvent event) {
            getHintDragCoordinates(event);
            boolean dispatched = mSeekBar.getTouchEventListener().onTouchEvent(mSeekBar, event);
            return dispatched;
        }
    };


    /**
     * isWidgetFullyVisible
     *
     * @param container container
     * @return WidgetFullyVisible
     */
    public abstract boolean isWidgetFullyVisible(Component container);

    /**
     * getHintDragCoordinates
     *
     * @param event event
     * @return Point
     */
    protected abstract Point getHintDragCoordinates(TouchEvent event);

    /**
     * getFixedHintOffset
     *
     * @return Point
     */
    protected abstract Point getFixedHintOffset();

    /**
     * getFollowHintOffset
     *
     * @return Point
     */
    protected abstract Point getFollowHintOffset();


    /**
     * showPopup
     */
    public void showPopup() {
        handler.removeAllEvent();
        handler.postTask(new Runnable() {
            @Override
            public void run() {
                showPopupInternally();
            }
        });
    }

    private void showPopupInternally() {
        switch (mPopupStyle) {
            case POPUP_FOLLOW:
                getFollowHintOffset();
                break;
            case POPUP_FIXED:
                getFixedHintOffset();
                break;
            default:
                throw new IllegalArgumentException("Popup style is unknown");
        }
    }

    /**
     * hidePopup
     */
    public void hidePopup() {
        handler.removeAllEvent();
    }

    /**
     * isPopupVisible
     *
     * @return boolean false
     */
    public boolean isPopupVisible() {
        return false;
    }

    /**
     * getSeekBar
     *
     * @return Slider对象
     */
    public Slider getSeekBar() {
        return mSeekBar;
    }

    /**
     * getPopupLayout
     *
     * @return PopupLayout布局id
     */
    public int getPopupLayout() {
        return mPopupLayout;
    }

    /**
     * setPopupLayout
     *
     * @param layout 布局id
     */
    public void setPopupLayout(int layout) {
        this.mPopupLayout = layout;
        initHintPopup();
        checkInitialState();
    }

    /**
     * getPopupStyle
     *
     * @return PopupStyle
     */
    public int getPopupStyle() {
        return mPopupStyle;
    }

    /**
     * setPopupStyle
     *
     * @param style PopupStyle
     */
    public void setPopupStyle(int style) {
        mPopupStyle = style;
        if (mPopupAlwaysShown) {
            showPopup();
        }
    }

    /**
     * isPopupAlwaysShown
     *
     * @return boolean值PopupAlwaysShown
     */
    public boolean isPopupAlwaysShown() {
        return mPopupAlwaysShown;
    }

    /**
     * setPopupAlwaysShown
     *
     * @param alwaysShown boolean值PopupAlwaysShown
     */
    public void setPopupAlwaysShown(boolean alwaysShown) {
        this.mPopupAlwaysShown = alwaysShown;
        if (alwaysShown) {
            showPopup();
            listener.onProgressUpdated(mSeekBar, mSeekBar.getProgress(), false);
        } else if (!isTracking) {
            hidePopup();
        } else {
            showPopup();
        }
    }

    /**
     * isPopupDraggable
     *
     * @return PopupDraggable
     */
    public boolean isPopupDraggable() {
        return mPopupDraggable;
    }

    /**
     * setPopupDraggable
     *
     * @param draggable draggable
     */
    public void setPopupDraggable(boolean draggable) {
        this.mPopupDraggable = draggable;
        if (mPopupView != null) {
            mPopupView.setTouchEventListener(draggable ? popupTouchProxy : null);
        }
    }

    /**
     * setHintAdapter
     *
     * @param adapter SeekBarHintAdapter
     */
    public void setHintAdapter(SeekBarHintAdapter adapter) {
        mHintAdapter = adapter;
        if (mPopupTextView != null) {
            mPopupTextView.setText(mHintAdapter.getHint(mSeekBar, mSeekBar.getProgress()));
        }
    }

    /**
     * setHintAttacher
     *
     * @param listener SeekBarHintAttacher
     */
    public void setHintAttacher(SeekBarHintAttacher listener) {
        this.mHintAttacher = listener;
    }

    /**
     * ProxyChangeListener
     */
    private static class ProxyChangeListener implements Slider.ValueChangedListener {
        private Slider.ValueChangedListener mInternalListener;
        private Slider.ValueChangedListener mExternalListener;

        /**
         * setInternalListener
         *
         * @param listener Slider.ValueChangedListener
         */
        void setInternalListener(Slider.ValueChangedListener listener) {
            mInternalListener = listener;
        }

        /**
         * setExternalListener
         *
         * @param listener Slider.ValueChangedListener
         */
        void setExternalListener(Slider.ValueChangedListener listener) {
            mExternalListener = listener;
        }

        @Override
        public void onProgressUpdated(Slider slider, int progress, boolean fromUser) {
            if (mInternalListener != null) {
                mInternalListener.onProgressUpdated(slider, progress, fromUser);
            }
            if (mExternalListener != null) {
                mExternalListener.onProgressUpdated(slider, progress, fromUser);
            }
        }

        @Override
        public void onTouchStart(Slider slider) {
            if (mInternalListener != null) {
                mInternalListener.onTouchStart(slider);
            }
            if (mExternalListener != null) {
                mExternalListener.onTouchStart(slider);
            }
        }

        @Override
        public void onTouchEnd(Slider slider) {
            if (mInternalListener != null) {
                mInternalListener.onTouchEnd(slider);
            }
            if (mExternalListener != null) {
                mExternalListener.onTouchEnd(slider);
            }
        }
    }

    /**
     * setOnSeekBarChangeListener
     *
     * @param valueChangedListener valueChangedListener
     * @return listener
     */
    public Slider.ValueChangedListener setOnSeekBarChangeListener(Slider.ValueChangedListener valueChangedListener) {
        if (valueChangedListener instanceof ProxyChangeListener) {
            listener = (ProxyChangeListener) valueChangedListener;
        } else {
            listener.setExternalListener(valueChangedListener);
        }
        return listener;
    }

    @Override
    public void onProgressUpdated(Slider slider, int progress, boolean fromUser) {
        String popupText = mHintAdapter.getHint(mSeekBar, progress);
        mPopupTextView.setText(popupText);
    }

    @Override
    public void onTouchStart(Slider slider) {
        isTracking = true;
        showPopupInternally();
    }

    @Override
    public void onTouchEnd(Slider slider) {
        isTracking = false;
        if (!mPopupAlwaysShown) {
            hidePopup();
        }
    }

    /**
     * getFollowPosition
     *
     * @return 位置
     */
    protected int getFollowPosition() {
        return getFollowPosition(mSeekBar.getProgress());
    }

    /**
     * getFollowPosition
     *
     * @param progress progress
     * @return 位置
     */
    protected int getFollowPosition(int progress) {
        return (int) (progress * (mSeekBar.getWidth() - mSeekBar.getPaddingLeft() - mSeekBar.getPaddingRight())
                / (float) mSeekBar.getMax());
    }

    /**
     * SeekBarHintDelegateHolder
     */
    public interface SeekBarHintDelegateHolder {
        /**
         * getHintDelegate
         *
         * @return ProgressHintDelegate
         */
        ProgressHintDelegate getHintDelegate();

        /**
         * getMarginLeftByPercent
         *
         * @return 左边距离
         */
        int getMarginLeftByPercent();

        /**
         * getMarginRightByPercent
         *
         * @return 右边距离
         */
        int getMarginRightByPercent();

        /**
         * getMarginTopByPercent
         *
         * @return 顶部距离
         */
        int getMarginTopByPercent();

        /**
         * getMarginBottomByPercent
         *
         * @return 底部距离
         */
        int getMarginBottomByPercent();
    }

    /**
     * SeekBarHintAttacher
     */
    public interface SeekBarHintAttacher {
        /**
         * onAttached
         */
        void onAttached();
    }

    /**
     * SeekBarHintAdapter
     */
    public interface SeekBarHintAdapter {
        /**
         * getHint
         *
         * @param seekBar seekBar
         * @param progress progress
         * @return String Hint
         */
        String getHint(Slider seekBar, int progress);
    }

    /**
     * MySeekBarHintAdapter
     */
    public static class MySeekBarHintAdapter implements SeekBarHintAdapter {
        @Override
        public String getHint(Slider seekBar, int progress) {
            return String.valueOf(progress);
        }
    };
}
